/*
 * Copyright (c) 2007 jMouse Gestures
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMouse Gestures' nor the names of its contributors 
 *   may be used to endorse or promote products derived from this software 
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jmg.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;


/**
 * IntArrayList class is the same as a regular <code>ArrayList</code> except
 * that in contains the primitive <code>int</code> type instead of an object.
 * <p>
 * This class does not use Java's Collection interfaces as they all require
 * objects instances.  This class however does mirror most of the methods
 * found in a regular <code>ArrayList</code>.
 * </p>
 *
 * @author  Christopher Martin
 * @version $Id: IntArrayList.java 12 2007-05-24 11:54:07Z GentlemanHal $
 */
public class IntArrayList implements
    Cloneable,
    Serializable
{
    /** Serial Version UID */
    private static final long serialVersionUID = 1L;
    
    private int[] buffer;
    private int lastElementIndex, initialCapacity;
    
    
    
    
    
    /**
     * Creates a new instance of <code>IntArrayList</code> with an initial
     * capacity of 10.
     */
    public IntArrayList() {
        this(10);
    }
    
    /**
     * Creates a new instance of <code>IntArrayList</code>.
     *
     * @param  initialCapacity The initial capacity of this 
     *         <code>IntArrayList</code>.
     * @throws java.lang.IllegalArgumentException If initial capacity is less
     *         than or equal to zero.
     */
    public IntArrayList(final int initialCapacity) 
        throws IllegalArgumentException
    {
        if (initialCapacity <= 0) {
            throw new IllegalArgumentException("initialCapacity must be > 0: " + 
                                                initialCapacity);
        }
        
        
        this.initialCapacity = initialCapacity;
        clear();
    }
    
    /**
     * Creates a new instance of <code>IntArrayList</code> that contains the
     * same elements as the given array and an initial capacity 10% larger.
     *
     * @param  array Adds the elements of the given array to this 
     *         <code>IntArrayList</code>.
     * @throws java.lang.NullPointerException If a <code>null</code> argument is
     *         passed.
     */
    public IntArrayList(final int[] array) 
        throws NullPointerException
    {
        this((int) Math.min((array.length * 110L) / 100, Integer.MAX_VALUE));
        
        System.arraycopy(array, 0, buffer, 0, array.length);
        lastElementIndex = array.length - 1;
    }
    
    /**
     * Creates a new instance of <code>IntArrayList</code> that contains the
     * same elements as the given <code>IntArrayList</code> and an initial 
     * capacity 10% larger.
     *
     * @param  list Adds the elements of the given <code>IntArrayList</code>
     *         to this <code>IntArrayList</code>.
     * @throws java.lang.NullPointerException If a <code>null</code> argument is
     *         passed.
     */
    public IntArrayList(final IntArrayList list) 
        throws NullPointerException
    {
        this(list.toArray());
    }

    
    
    
    
    /**
     * Gets the int stored at the given index. Index should be:
     * <p>
     * <code>0 <= index <= {@link #size() size} - 1</code>.
     * 
     * @param  index The index of the value to return.
     * @throws java.lang.IllegalArgumentException If index is less than zero or
     *         greater than or equal to the {@link #size() size} of this 
     *         <code>IntArrayList</code>.
     * @return The value at the given index.
     */
    public int get(final int index) 
        throws IllegalArgumentException
    {
        if (index < 0 || index >= size()) {
            throw new IllegalArgumentException("Illegal index: " + index);
        }
            
            
        return buffer[index];
    }

    /**
     * Returns the size of this <code>IntArrayList</code>.
     * 
     * @return The size.
     */
    public int size() {
        return lastElementIndex + 1;
    }

    /**
     * Returns the index of the first matching element or -1 if no matching
     * element is found.
     * 
     * @param  element The value of the element to find.
     * @return The index of the element or -1.
     */
    public int indexOf(final int element) {
        for (int i = 0; i < size(); i++) {
            if (buffer[i] == element) {
                return i;
            }
        }
        
        return -1;
    }

    /**
     * Returns if this <code>IntArrayList</code> contains the given element or 
     * not.
     * 
     * @param  element The element to look for.
     * @return <code>true</code> if this list contains the element,
     *         <code>false</code> otherwise.
     */
    public boolean contains(final int element) {
        return indexOf(element) >= 0;
    }

    /**
     * Adds the given element to the end of this <code>IntArrayList</code>.
     * 
     * @param  element The element to add.
     * @return <code>true</code> if the element was added, <code>false</code>
     *         otherwise.
     */
    public boolean add(final int element) {
        ensureCapacity(size() + 1);
	buffer[++lastElementIndex] = element;
        
	return true;
    }

    /**
     * Returns the index of the last occurrence of the specified 
     * <code>int</code> in this <code>IntArrayList</code>.
     * 
     * @param  element The element to find.
     * @return The index of the last occurrence of the specified 
     *         <code>int</code> in this <code>IntArrayList</code>, returns -1 
     *         if the <code>int</code> is not found.
     */
    public int lastIndexOf(final int element) {
        for (int i = size(); --i >= 0;) {
            if (buffer[i] == element) {
                return i;
            }
        }
        
        return -1;
    }

    /**
     * Removes a single instance of the specified element from this
     * <code>IntArrayList</code>, if it is present.
     * 
     * @param  element The element to remove.
     * @return <code>true</code> if the list contained the specified element 
     *         (or equivalently, if the list changed as a result of the call).
     */
    public boolean remove(final int element) {
        int index = indexOf(element);
        
        if (index >= 0) {
            System.arraycopy(buffer, index + 1, buffer, index, size() - index);
            
            lastElementIndex--;
            
            return true;
        } else {
            return false;
        }
        
    }

    /**
     * Checks to see if this <code>IntArrayList</code> contains all the passed
     * elements.
     * 
     * @param  elements The elements to check for.
     * @return <code>true</code> if this list contains all the passed elements,
     *         <code>false</code> otherwise.
     */
    public boolean containsAll(final int[] elements) {
        for (int i = 0; i < elements.length; i++) {
            if (indexOf(elements[i]) < 0) {
                return false;
            }
        }
        
        return true;
    }

    /**
     * Removes all the given elements.
     * 
     * @param  elements The elements to remove.
     * @return <code>true</code> if all the given elements are removed,
     *         <code>false</code> otherwise.
     */
    public boolean removeAll(final int[] elements) {
        boolean ret = true;
        
        for (int i = 0; i < elements.length; i++) {
            if (!remove(elements[i])) {
                ret = false;
            }
        }
        
        return ret;
    }

    /**
     * Increases the capacity of this <code>IntArrayList</code> instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument. 
     * 
     * @param minCapacity The desired minimum capacity.
     */
    public void ensureCapacity(final int minCapacity) {
        if (minCapacity > buffer.length) {
            int[] temp = new int[minCapacity];
            
            System.arraycopy(buffer, 0, temp, 0, buffer.length);
            
            buffer = temp;
        }
    }

    /**
     * Adds the given element at the specified index.  Index should be:
     * <p>
     * <code>0 <= index <= {@link #size() size} - 1</code>.
     * 
     * @param  index The index to add at.
     * @param  element The value to add at that index.
     * @throws java.lang.IllegalArgumentException If index is less than zero or
     *         greater than or equal to {@link #size size}.
     */
    public void add(final int index, final int element) 
        throws IllegalArgumentException
    {
        if (index < 0 || index >= size()) {
            throw new IllegalArgumentException("Illegal index: " + index);
        }
        
        
        ensureCapacity(size() + 1);
        
        System.arraycopy(buffer, index, buffer, index + 1, size() - index);
        
        buffer[index] = element;
        lastElementIndex++;
    }

    /**
     * Sets the value at the given index to the given element.  Index should be:
     * <p>
     * <code>0 <= index <= {@link #size() size} - 1</code>.
     * 
     * @param  index The index to set.
     * @param  element The value to set.
     * @throws java.lang.IllegalArgumentException If index is less than zero or
     *         greater than or equal to {@link #size size}.
     * @return The old value at the given index.
     */
    public int set(final int index, final int element) 
        throws IllegalArgumentException
    {
        if (index < 0 || index >= size()) {
            throw new IllegalArgumentException("Illegal index: " + index);
        }
            
            
        int old = buffer[index];
        
        buffer[index] = element;
        
        return old;
    }

    /**
     * Adds all the given elements to this <code>IntArrayList</code>.
     * 
     * @param  elements The elements to add.
     * @return <code>true</code> if all the elements were added,
     *         <code>false</code> otherwise.
     */
    public boolean addAll(final int[] elements) {
        boolean ret = true;
        
        for (int i = 0; i < elements.length; i++) {
            if (!add(elements[i])) {
                ret = false;
            }
        }
        
        return ret;
    }

    /**
     * Trims the capacity of this <code>IntArrayList</code> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <code>IntArrayList</code> instance.
     */
    public void trimToSize() {
        if (size() < buffer.length) {
            int[] temp = new int[size()];

            System.arraycopy(buffer, 0, temp, 0, temp.length);

            buffer = temp;
        }
    }

    /**
     * Tests if this <code>IntArrayList</code> has no elements.
     * 
     * @return <code>true</code> if this list has no elements,
     *         <code>false</code> otherwise.
     */
    public boolean isEmpty() {
        return lastElementIndex < 0;
    }

    /**
     * Clears all elements from this <code>IntArrayList</code>.
     */
    public void clear() {
        buffer = new int[initialCapacity];
        lastElementIndex = -1;
    }

    /**
     * Returns a sub list of this list from fromIndex (inclusive) to toIndex
     * (exclusive). 
     * 
     * @param  fromIndex The element to start from, inclusive.
     * @param  toIndex The last element to copy, exclusive.
     * @throws java.lang.IllegalArgumentException If fromIndex is less than zero
     *         or fromIndex is greater than toIndex or if toIndex is greater
     *         than {@link #size size}.
     * @return The sub list.
     */
    public int[] subList(final int fromIndex, final int toIndex) 
        throws IllegalArgumentException
    {
        if (fromIndex < 0 || fromIndex > toIndex) {
            throw new IllegalArgumentException("Illegal fromIndex: " + fromIndex);
        }
        if (toIndex > size()) {
            throw new IllegalArgumentException("Illegal toIndex: " + toIndex);
        }
        
        
        int[] ret = new int[toIndex - fromIndex];
        
        System.arraycopy(buffer, fromIndex, ret, 0, ret.length);
        
        return ret;
    }

    /**
     * Returns this <code>IntArrayList</code> as an array.  Changes to the
     * returned array will not effect this instance.
     * 
     * @return The array.
     */
    public int[] toArray() {
        int[] ret = new int[size()];
        
        System.arraycopy(buffer, 0, ret, 0, ret.length);
        
        return ret;
    }
    
    /**
     * Returns an <code>ArrayList</code> instance containing the same values
     * as this list.  Changes to the returned <code>ArrayList</code> will not 
     * effect this instance.
     * 
     * @return The <code>ArrayList</code>.
     */
    public ArrayList<Integer> toArrayList() {
        ArrayList<Integer> ret = new ArrayList<Integer>();
        
        for (int element : toArray()) {
            ret.add(element);
        }
        
        return ret;
    }

    
    
    
    
    /**
     * Returns a <code>String</code> representation of this instance.
     * 
     * @return The <code>String</code> representation.
     */
    @Override
    public String toString() {
        return getClass().getName() + Arrays.toString(toArray());
    }

    /**
     * Indicates whether some other object is "equal to" this one.
     * 
     * @param  obj The reference object with which to compare.
     * @return <code>true</code> if this object is the same as the obj
     *         argument; <code>false</code> otherwise.
     * @see    #hashCode()
     * @see    java.util.Hashtable
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        
        if (null == obj || obj.getClass() != getClass()) {
            return false;
        }
            
        return Arrays.equals(toArray(), ((IntArrayList) obj).toArray());
    }

    /**
     * Creates and returns a copy of this <code>IntArrayList</code> that
     * contains all the same elements.  Changes to the returned 
     * <code>IntArrayList</code> will not effect this instance.
     * 
     * @return A clone of this instance.
     * @see    java.lang.Cloneable
     */
    @Override
    public Object clone() 
        throws CloneNotSupportedException 
    {
        IntArrayList ret = (IntArrayList) super.clone();
        
        ret.buffer = buffer.clone();
        ret.initialCapacity = initialCapacity;
        ret.lastElementIndex = lastElementIndex;
        
        return ret;
    }

    /**
     * Returns a hash code value for the object.
     * 
     * @return A hash code value for this object.
     * @see    java.lang.Object#equals(java.lang.Object)
     * @see    java.util.Hashtable
     */
    @Override
    public int hashCode() {
        return Arrays.hashCode(toArray());
    }
    
}
